#include "rt_math.h"

namespace raytracer
{

	/****************************************************************************
	IsSmall()

	Returns true if value is smaller than a determined small epsilon.
	*****************************************************************************/
	bool raytracer::IsSmall(double num)
	{
		return Sign(num) * num < SMALL_VAL;
	} // end IsSmall()
	/****************************************************************************
	Sign()

	Returns 1 if positive, 0 if zero, -1 if negative.
	*****************************************************************************/
	int raytracer::Sign(double num)
	{
		return (num < 0.0 ? -1 : num > 0.0);
	} // end Sign()
	/****************************************************************************
	Clamp()

	Returns a value clamped in between l and h.
	*****************************************************************************/
	double raytracer::Clamp(double n, double l, double h)
	{
		if (l > h)
			return (n < h) ? h : ((n > l) ? l : n);
		else
			return (n > h) ? h : ((n < l) ? l : n);
	} // end Clamp()
	/****************************************************************************
	Divide()

	A safe divide, which prevents division by zero.
	*****************************************************************************/
	double raytracer::Divide(double n, double d)
	{
		// If determinator is small or numerator is large,
		// just return predetermined large value.
		if (IsSmall(d))
		{
			return Sign(d) * Sign(n) * LARGE_VAL;
		}
		else
		{
			return (n < LARGE_VAL - d && n > - LARGE_VAL - d) ? n / d
				: LARGE_VAL * Sign(n) * Sign(d);
		}
	} // end Divide()
	/****************************************************************************
	SolveLinear()

	Solve a linear equation and return number of solutions.
	*****************************************************************************/
	int raytracer::SolveLinear(double c0, double c1, double* pSolution)
	{
		// If linear term is too small, then there is no solution.
		if (IsSmall(c1))
		{
			return 0;
		}
		else
		{
			*pSolution = Divide(-c0, c1);

			return 1;
		}
	} // end SolveLinear()
	/****************************************************************************
	SolveQuadratic()

	Solve a quadratic equation and return number of solutions.
	*****************************************************************************/
	int raytracer::SolveQuadratic(double c0, double c1, double c2, double* pSolutions)
	{
		// If quadratic term is small, then it's roughly linear.
		if (IsSmall(c2))
		{
			return SolveLinear(c0, c1, pSolutions);
		}
		else
		{
			double d = c1 * c1 - 4 * c0 * c2;
			// If discriminant is zero (or sufficiently small), there is only one distinct solution.
			if (IsSmall(d))
			{
				*pSolutions = Divide(-c1, 2 * c2);

				return 1;
			}
			else
			{
				// If discriminant is negative, there are no real solutions.
				if (d < 0)
				{
					return 0;
				}
				else
				{
					pSolutions[0] = Divide(-c1 - std::sqrt(d), 2 * c2);
					pSolutions[1] = Divide(-c1 + std::sqrt(d), 2 * c2);

					return 2;
				}
			}
		}
	} // end SolveQuadratic()
}